﻿using System;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace DriveManage.Modbus
{
    public class ModbusService
    {


        /// <summary>
        /// 
        /// DEMO
        ///  // 使用串口通讯（RS - 232/RS - 485）
        /// ModbusSerial modbusSerial = new ModbusSerial(ModbusEnum.ModbusSerialPort,null,"COM1", 9600, Parity.None, 8, StopBits.One);
        /// modbusSerial.Open();
        /// 从从站地址为 1 的设备，起始地址 0 开始，读取 10 个保持寄存器的数据
        /// byte[] serialResponse = modbusSerial.ReadHoldingRegisters(1, 0, 10);
        /// modbusSerial.Close();
        /// 
        ///  // 使用以太网通讯（Modbus TCP）
        /// ModbusTcp modbusTcp = new ModbusTcp(ModbusEnum.ModbusTcp,"192.168.1.100", 502);
        /// byte[] tcpResponse = modbusTcp.ReadHoldingRegisters(1, 0, 10);
        /// modbusTcp.Close();
        /// 
        /// 
        /// </summary>

        private SerialPort serialPort;
        private ModbusEnum modbusEnum;
        private TcpClient tcpClient;
        private NetworkStream networkStream;
        public byte[] DataBuffer;
        public string DataStringTemp;
        public string DataString;

        public ModbusService() { }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cp"></param>
        /// <param name="IP"></param>
        /// <param name="Port">串口号或端口号</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="parity">校验位</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        public ModbusService(ModbusEnum cp, string IP, string Port, int baudRate, Parity parity, int dataBits, StopBits stopBits)
        {
            modbusEnum = cp;
            switch (modbusEnum)
            {
                case ModbusEnum.ModbusSerialPort:
                    stopBits = StopBits.One;
                    serialPort = new SerialPort(Port, baudRate, parity, dataBits, stopBits);
                    serialPort.DataReceived += SerialPort_DataReceived;
                    break;
                case ModbusEnum.ModbusTcp:
                    tcpClient = new TcpClient();
                    tcpClient.Connect(IPAddress.Parse(IP), int.Parse(Port));
                    networkStream = tcpClient.GetStream();
                    break;
            }
        }

        /// <summary>
        /// 处理接收到的数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            byte[] buffer = new byte[sp.BytesToRead];
            sp.Read(buffer, 0, buffer.Length);
            ProcessReceivedData(buffer);
        }

        /// <summary>
        /// 实现数据处理逻辑，例如解析 Modbus 数据
        /// </summary>
        /// <param name="data"></param>
        private void ProcessReceivedData(byte[] data)
        {
            DataBuffer = data;
            string temp3 = Encoding.UTF8.GetString(data);
            if (temp3.Contains("\r\n"))
            {
                DataString = DataStringTemp;
                DataStringTemp = "";
            }
            else
            {
                string temp = temp3.Replace("\r\n", "");
                DataStringTemp += temp;
            }
        }

        public void Open()
        {
            if (!serialPort.IsOpen)
            {
                serialPort.Open();
            }
        }

        public void Close()
        {
            switch (modbusEnum)
            {
                case ModbusEnum.ModbusSerialPort:
                    if (serialPort.IsOpen)
                    {
                        serialPort.DataReceived -= SerialPort_DataReceived;
                        serialPort.Close();
                    }
                    break;
                case ModbusEnum.ModbusTcp:
                    if (networkStream != null)
                    {
                        networkStream.Close();
                    }
                    if (tcpClient != null)
                    {
                        tcpClient.Close();
                    }
                    break;
            }
        }

        /// <summary>
        /// 读取保持寄存器的值
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="quantity"></param>
        /// <returns></returns>
        public byte[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort quantity)
        {
            byte[] request = null;
            byte[] RES = null;
            switch (modbusEnum)
            {
                case ModbusEnum.ModbusSerialPort:
                    request = BuildReadRequest(slaveAddress, 0x03, startAddress, quantity);
                    serialPort.Write(request, 0, request.Length);
                    RES = new byte[serialPort.BytesToRead];
                    serialPort.Read(RES, 0, RES.Length);
                    //return RES;
                    break;
                case ModbusEnum.ModbusTcp:
                    request = BuildTcpRequest(slaveAddress, 0x03, startAddress, quantity);
                    networkStream.Write(request, 0, request.Length);
                    RES = new byte[256];
                    int bytesRead = networkStream.Read(RES, 0, RES.Length);
                    byte[] actualResponse = new byte[bytesRead];
                    Array.Copy(RES, actualResponse, bytesRead);
                    //   return actualResponse;
                    break;
            }
            return RES;
        }


        private byte[] BuildReadRequest(byte slaveAddress, byte functionCode, ushort startAddress, ushort quantity)
        {
            byte[] request = new byte[8];
            request[0] = slaveAddress;
            request[1] = functionCode;
            request[2] = (byte)(startAddress >> 8);
            request[3] = (byte)startAddress;
            request[4] = (byte)(quantity >> 8);
            request[5] = (byte)quantity;
            ushort crc = CalculateCRC(request, 6);
            request[6] = (byte)(crc & 0xFF);
            request[7] = (byte)(crc >> 8);
            return request;
        }

        /// <summary>
        /// Modbus RTU 请求帧的 CRC 校验码
        /// </summary>
        /// <param name="data"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private ushort CalculateCRC(byte[] data, int length)
        {
            ushort crc = 0xFFFF;
            for (int i = 0; i < length; i++)
            {
                crc ^= data[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) == 1)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }
            return crc;
        }

        private byte[] BuildTcpRequest(byte slaveAddress, byte functionCode, ushort startAddress, ushort quantity)
        {
            byte[] request = new byte[12];
            // Transaction ID
            request[0] = 0x00;
            request[1] = 0x01;
            // Protocol ID
            request[2] = 0x00;
            request[3] = 0x00;
            // Length
            request[4] = 0x00;
            request[5] = 0x06;
            // Unit ID
            request[6] = slaveAddress;
            request[7] = functionCode;
            request[8] = (byte)(startAddress >> 8);
            request[9] = (byte)startAddress;
            request[10] = (byte)(quantity >> 8);
            request[11] = (byte)quantity;
            return request;
        }

    }
}
